package com.googlecode.tda367.denty.core;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;

import com.googlecode.tda367.denty.core.camera.Camera;
import com.googlecode.tda367.denty.core.dynamicbody.DynamicBody;
import com.googlecode.tda367.denty.core.level.FastLevel;
import com.googlecode.tda367.denty.core.level.Level;
import com.googlecode.tda367.denty.core.level.PuzzleLevel;
import com.googlecode.tda367.denty.core.level.TutorialLevel;


public class DentyModel implements PropertyChangeListener{
	private static DentyModel instance;
	private Input input;
	private Level level;
	private ArrayList<Class<? extends Level>> levelList;
	private Vec2 mousePressedPos;
	private GameContainer container;
	private boolean throwing;
	
	public Vec2 getMousePressedPos() {
		return mousePressedPos;
	}

	private DentyModel() {
		// private constructor to ensure the class is a singleton class
	}

	public static DentyModel getInstance(){
		if (instance == null) {
			DentyModel.instance = new DentyModel();
		}
		return  instance;
	}

	public void init(GameContainer container) {
		this.container=container;
		this.input = container.getInput();
		levelList = new ArrayList<Class<? extends Level>>();
		levelList.add(TutorialLevel.class);
		levelList.add(FastLevel.class);
		levelList.add(PuzzleLevel.class);
		this.loadNextLevel();
		Settings.linearSlop = 0.0001f;
	}


	public void update(GameContainer container, int delta) {
		
		level.update();	
	}
	
	public boolean canReleaseBody(float x, float y){
		return level.canReleaseBody(x + 0.2f, y + 0.2f, x + 0.8f, y + 0.8f);
	}
	
	public void releaseBody(int x, int y){
		if(this.canReleaseBody(x, y))
			level.releaseBodies(x + 0.2f, y + 0.2f, x + 0.8f, y + 0.8f);
	}

	public Level getLevel() {
		return level;
	}

	public List<DynamicBody> getDynamicBodies() {
		return this.level.getDynamicBodies();
	}

	public boolean canPlace(float x, float y) {
		return level.canAddBlock() && level.isAreaFree(x + 0.2f, y + 0.2f, x + 0.8f, y + 0.8f);
	}
	
	public Vec2 getDentyPosition(){
		return new Vec2(level.getDenty().getPosition());
	}

	public boolean canThrow(int x, int y) {
		return this.canPlace(x, y);
	}
	
	public void setThrow(boolean b){
		this.throwing = b;
	}
	
	public boolean willThrow(){
		return this.throwing;
	}
	
	private void loadNextLevel(){
		if (!levelList.isEmpty()){
			Class<? extends Level> nextLevel = levelList.get(0);
			levelList.remove(0);
			try {
				this.level = nextLevel.newInstance();
			} catch (Exception e) {
				handleLoadingError(nextLevel);
			} catch (Error e) {
				handleLoadingError(nextLevel);
			}
		}
	}
	
	private <T extends Level> void handleLoadingError(Class<T> level){
		System.out.println("Couldn't load level \""+level.getSimpleName()+"\"" +
				(levelList.isEmpty() ? "." : ", will try to load level \""+levelList.get(0).getSimpleName()+"\"."));
		if (!levelList.isEmpty())
			loadNextLevel();
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt){
		if (evt.getPropertyName() == "killed"){
			level.restart();
		}
		else if (evt.getPropertyName() == "Goal!"){
			if (levelList.isEmpty()){
				System.out.println("Game complete! Congratulations!");
				container.exit();
			}
			else{
				this.loadNextLevel();
			}
		}
	}

	public float getScreenHeight() {
		return this.container.getScreenHeight();
	}

	public float getScreenWidth() {
		return this.container.getScreenWidth();
	}
}
